package tests;

import static org.junit.Assert.*;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import parsing.CritterParser;
import parsing.Parser;
import parsing.productions.MulOp;
import parsing.productions.Node;
import parsing.productions.Program;

public class NodeTest {

	private static Reader input;
	private static Writer output;
	private static Parser parser;
	
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		parser = new CritterParser();
	}
	
	@Before
	public void setUp(){
		try{
			input = new FileReader(new File("testInput.txt"));
		} catch (IOException e) {
			System.out.println(e.getMessage());
		}
	}

	/**
	 * Tests that the duplicate() method returns correctly
	 * Note: this also briefly tests size()
	 */
	@Test
	public void duplicateTest() {
		Node program = parser.parse(input);
		Node duplicateProgram = program.duplicate();
		int programSize = program.size();
		assertTrue("Duplicate does not create a Tree of the same size.", programSize == duplicateProgram.size());
		assertFalse("Duplicate points to the original tree.", duplicateProgram == program);
		program = new MulOp(MulOp.TIMES);
		assertTrue("Size does not work for small Trees.", program.size() == 1);
		assertTrue("Duplicate affects the original tree.", duplicateProgram.size() == programSize);
	}

	/**
	 * Tests that mutate() makes one and only one mutation. Files must be checked.
	 */
	@Test
	public void oneMutation(){
		try {
			Program program = parser.parse(input);
			Program mutatedProgram = program.mutate();
			StringBuffer buf = new StringBuffer();
			program.prettyPrint(buf);
			output = new PrintWriter(new File("originalProgram.txt"));
			output.write(buf.toString());
			output.close();
			buf = new StringBuffer();
			mutatedProgram.prettyPrint(buf);
			output = new PrintWriter(new File("mutatedProgram.txt"));
			output.write(buf.toString());
			output.close();
		} catch (IOException e) {
			System.out.println(e.getMessage());
		}
	}
	
	/**
	 * Tests that a program can be mutated many times over without errors occurring
	 * will cause exceptions upon failure
	 */
	@Test
	public void manyMutations(){
		Program program = parser.parse(input);
			for(int i = 0; i < 100; i++){
				Program.mutate(program);
			}
		StringBuffer sb = new StringBuffer();
		program.prettyPrint(sb);
		try {
			output = new PrintWriter(new File("testOutput.txt"));
			output.write(sb.toString());
			output.close();
		} catch (IOException e) {
			System.out.println(e.getMessage());
		}
	}
	
	/**
	 * Tests that mutated programs are still valid, parsable programs
	 */
	@Test
	public void parsableMutation(){
		Program program = parser.parse(input);
		StringBuffer sb;
		for(int i = 0; i < 20; i++){
			Program.mutate(program);
			sb = new StringBuffer();
			program.prettyPrint(sb);
			try {
				output = new PrintWriter(new File("program.txt"));
				output.write(sb.toString());
				output.close();
				program = parser.parse(new FileReader(new File("program.txt")));
			} catch (IOException e) {
				System.out.println(e.getMessage());
			}
		}
		
	}
}
